package com.autumn.zero.authorization.services.impl;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.commons.collections.map.CaseInsensitiveMap;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;

import com.autumn.domain.services.AbstractDomainService;
import com.autumn.mybatis.criterion.EntityQuery;
import com.autumn.mybatis.criterion.Query;
import com.autumn.runtime.cache.AutumnCacheContext;
import com.autumn.util.AutoMapUtils;
import com.autumn.util.CollectionUtils;
import com.autumn.util.StringUtils;
import com.autumn.zero.authorization.entitys.IPermissionEntity;
import com.autumn.zero.authorization.entitys.modules.ResourcesModule;
import com.autumn.zero.authorization.entitys.modules.ResourcesModulePermission;
import com.autumn.zero.authorization.repositorys.modules.IResourcesModulePermissionRepository;
import com.autumn.zero.authorization.repositorys.modules.IResourcesModuleRepository;
import com.autumn.zero.authorization.services.IResourcesService;
import com.autumn.zero.authorization.values.ResourcesModuleOperationPermissionValue;
import com.autumn.zero.authorization.values.ResourcesModulePermissionTreeValue;
import com.autumn.zero.authorization.values.ResourcesModuleTreeValue;

/**
 * 
 * 资源服务
 * 
 * @author 杨昌国 2018-12-05 18:49:47
 */
public class ResourcesServiceImpl extends AbstractDomainService implements IResourcesService {

	/**
	 * 资源模块缓存名称
	 */
	private static final String CACHE_NAME_RESOURCES_MODULE = "resources_module";

	/**
	 * 资源模块菜单缓存名称
	 */
	private static final String CACHE_NAME_RESOURCES_MODULE_MENU = "resources_module_menu";

	/**
	 * 资源模块权限缓存名称
	 */
	private static final String CACHE_NAME_RESOURCES_MODULE_PERMISSION = "resources_module_permission";

	/**
	 * 资源模块 shiro 权限缓存名称
	 */
	private static final String CACHE_NAME_RESOURCES_MODULE_PERMISSION_SHIRO = "resources_module_permission_shiro";

	@Autowired
	private IResourcesModuleRepository resourcesModuleRepository;

	@Autowired
	private IResourcesModulePermissionRepository resourcesModulePermissionRepository;

	@Cacheable(cacheNames = CACHE_NAME_RESOURCES_MODULE_PERMISSION_SHIRO)
	@Override
	public Map<String, String> shiroPermissionFilterMap() {
		Map<String, String> filterMap = new LinkedHashMap<>();
		Map<String, ResourcesModule> resourcesModuleMap = this.queryAllResourcesModuleMap();
		List<ResourcesModulePermission> permissions = this.queryAllModulePermission();
		for (ResourcesModulePermission permission : permissions) {
			this.putShiroFilterMap(filterMap, permission, resourcesModuleMap);
		}
		return filterMap;
	}

	/**
	 * 
	 * @param filterMap
	 * @param permission
	 * @param resourcesModuleMap
	 */
	private void putShiroFilterMap(Map<String, String> filterMap, ResourcesModulePermission permission,
			Map<String, ResourcesModule> resourcesModuleMap) {
		if (!StringUtils.isNullOrBlank(permission.getPermissionUrl())) {
			String[] permissionArray = permission.getPermissionUrl().trim().split("\r|\n");
			ResourcesModule moule = resourcesModuleMap.get(permission.getResourcesId());
			if (moule != null) {
				String permissionName = String.format("%s:%s", permission.getResourcesId(), permission.getName())
						.toLowerCase();
				for (String item : permissionArray) {
					String urlPath = moule.getPermissionUrl() + StringUtils.removeStart(item, '/');
					filterMap.put(urlPath, String.format("perms[%s]", permissionName));
				}
			}
		}
	}

	/**
	 * 查询模块所有权限
	 * 
	 * @return
	 */
	private List<ResourcesModulePermission> queryAllModulePermission() {
		EntityQuery<ResourcesModulePermission> query = new EntityQuery<>(ResourcesModulePermission.class);
		query.notEq(ResourcesModulePermission.FILED_PERMISSION_URL, "").orderBy("id");
		return query.selectByList(this.resourcesModulePermissionRepository);
	}

	/**
	 * 查询资源授权的所有模块
	 * 
	 * @return
	 */
	private Map<String, ResourcesModule> queryAllResourcesModuleMap() {
		@SuppressWarnings("unchecked")
		Map<String, ResourcesModule> map = new CaseInsensitiveMap();
		EntityQuery<ResourcesModule> query = new EntityQuery<>(ResourcesModule.class);
		query.orderBy("id");
		List<ResourcesModule> modules = query.selectByList(this.resourcesModuleRepository);
		for (ResourcesModule module : modules) {
			if (!StringUtils.isNullOrBlank(module.getPermissionUrl())) {
				module.setPermissionUrl(StringUtils.removeEnd(module.getPermissionUrl(), '/') + "/");
			} else {
				module.setPermissionUrl("/");
			}
			map.put(module.getId(), module);
		}
		return map;
	}

	@Override
	public <P extends IPermissionEntity> List<ResourcesModuleTreeValue> filterModuleTree(
			List<ResourcesModuleTreeValue> moduleTree, List<P> permissions) {
		List<ResourcesModuleTreeValue> result = new ArrayList<>();
		if (moduleTree == null || moduleTree.size() == 0) {
			return result;
		}
		for (ResourcesModuleTreeValue source : moduleTree) {
			ResourcesModuleTreeValue treeValue = this.newModuleTree(source, permissions);
			if (treeValue != null) {
				result.add(treeValue);
			}
		}
		return result;
	}

	private <P extends IPermissionEntity> ResourcesModuleTreeValue newModuleTree(ResourcesModuleTreeValue source,
			List<P> matchPermissions) {
		boolean isGranted;
		if (source.getIsAuthorize()) {
			if (matchPermissions == null || matchPermissions.size() == 0) {
				isGranted = false;
			} else {
				isGranted = matchPermissions.stream().anyMatch(p -> {
					return p.getIsGranted() && p.getResourcesId().equalsIgnoreCase(source.getId())
							&& StringUtils.isNullOrBlank(p.getName());
				});
			}
		} else {
			isGranted = true;
		}
		if (!isGranted) {
			return null;
		}
		ResourcesModuleTreeValue destination = new ResourcesModuleTreeValue();
		AutoMapUtils.map(source, destination);
		destination.setChildren(null);
		if (source.getChildren() != null && source.getChildren().size() > 0) {
			for (ResourcesModuleTreeValue child : source.getChildren()) {
				ResourcesModuleTreeValue treeValue = this.newModuleTree(child, matchPermissions);
				if (treeValue != null) {
					destination.childrenNullValueInit();
					destination.getChildren().add(treeValue);
				}
			}
		}
		return destination;
	}

	@Cacheable(cacheNames = CACHE_NAME_RESOURCES_MODULE_MENU)
	@Override
	public List<ResourcesModuleTreeValue> queryByMenuTree(int resourcesType) {
		Query query = new Query(ResourcesModule.class);
		query.eq(ResourcesModule.FILED_RESOURCES_TYPE, resourcesType).eq(ResourcesModule.FILED_IS_MENU, true)
				.orderBy(ResourcesModule.FILED_PARENT_ID).orderBy(ResourcesModule.FILED_SORT_ID)
				.orderBy(ResourcesModule.FILED_ID);
		return this.queryModuleByTree(query);
	}

	@Cacheable(cacheNames = CACHE_NAME_RESOURCES_MODULE)
	@Override
	public List<ResourcesModuleTreeValue> queryByTree() {
		Query query = new Query(ResourcesModule.class);
		query.orderBy(ResourcesModule.FILED_PARENT_ID).orderBy(ResourcesModule.FILED_SORT_ID)
				.orderBy(ResourcesModule.FILED_ID);
		return this.queryModuleByTree(query);
	}

	@Cacheable(cacheNames = CACHE_NAME_RESOURCES_MODULE)
	@Override
	public List<ResourcesModuleTreeValue> queryByTree(int resourcesType) {
		Query query = new Query(ResourcesModule.class);
		query.eq(ResourcesModule.FILED_RESOURCES_TYPE, resourcesType).orderBy(ResourcesModule.FILED_PARENT_ID)
				.orderBy(ResourcesModule.FILED_SORT_ID).orderBy(ResourcesModule.FILED_ID);
		return this.queryModuleByTree(query);
	}

	private List<ResourcesModuleTreeValue> queryModuleByTree(Query query) {
		List<ResourcesModule> modules = resourcesModuleRepository.selectList(query.builderSection());
		List<ResourcesModuleTreeValue> result = new ArrayList<>();
		modules.forEach(module -> {
			if (StringUtils.isNullOrBlank(module.getParentId())) {
				result.addAll(findModuleChilds(module, modules, 0, null));
			}
		});
		return result;
	}

	/**
	 * 递归树
	 * 
	 * @param item
	 * @param modules
	 * @param level
	 * @param parentId
	 * @return
	 */
	private List<ResourcesModuleTreeValue> findModuleChilds(ResourcesModule item, List<ResourcesModule> modules,
			int level, ResourcesModuleTreeValue parent) {
		List<ResourcesModule> moduleList = CollectionUtils.findCollection(modules, (s) -> {
			return item.getId().equalsIgnoreCase(s.getParentId());
		});
		List<ResourcesModuleTreeValue> result = new ArrayList<>();
		ResourcesModuleTreeValue moduleValue = AutoMapUtils.map(item, ResourcesModuleTreeValue.class);
		if (moduleList.size() > 0) {
			moduleValue.childrenNullValueInit();
		}
		moduleValue.setPath(new ArrayList<>());
		if (parent != null) {
			// 先加载父级路径
			if (parent.getPath() != null) {
				moduleValue.getPath().addAll(parent.getPath());
			}
		}
		moduleValue.getPath().add(moduleValue.getId());
		for (ResourcesModule module : moduleList) {
			moduleValue.getChildren().addAll(findModuleChilds(module, modules, level + 1, moduleValue));
		}
		result.add(moduleValue);
		return result;
	}

	@Cacheable(cacheNames = CACHE_NAME_RESOURCES_MODULE_PERMISSION)
	@Override
	public List<ResourcesModulePermissionTreeValue> queryByPermissionTree() {
		EntityQuery<ResourcesModule> query = new EntityQuery<>(ResourcesModule.class);
		query.orderBy(ResourcesModule.FILED_PARENT_ID).eq(ResourcesModule.FILED_IS_AUTHORIZE, true)
				.orderBy(ResourcesModule.FILED_SORT_ID).orderBy(ResourcesModule.FILED_ID);
		return this.queryByPermissionTree(query);
	}

	@Cacheable(cacheNames = CACHE_NAME_RESOURCES_MODULE_PERMISSION)
	@Override
	public List<ResourcesModulePermissionTreeValue> queryByPermissionTree(int resourcesType) {
		EntityQuery<ResourcesModule> query = new EntityQuery<>(ResourcesModule.class);
		query.eq(ResourcesModule.FILED_RESOURCES_TYPE, resourcesType).eq(ResourcesModule.FILED_IS_AUTHORIZE, true)
				.orderBy(ResourcesModule.FILED_PARENT_ID).orderBy(ResourcesModule.FILED_SORT_ID)
				.orderBy(ResourcesModule.FILED_ID);
		return this.queryByPermissionTree(query);
	}

	/**
	 * 
	 * @param query
	 * @return
	 */
	private List<ResourcesModulePermissionTreeValue> queryByPermissionTree(EntityQuery<ResourcesModule> query) {
		List<ResourcesModule> modules = query.selectByList(this.resourcesModuleRepository);
		// 查询所有模块定义的权限
		EntityQuery<ResourcesModulePermission> modulePermissionQuery = new EntityQuery<>(
				ResourcesModulePermission.class);
		modulePermissionQuery.orderBy(ResourcesModulePermission.FILED_RESOURCES_ID)
				.orderBy(ResourcesModulePermission.FILED_SORT_ID).orderBy(ResourcesModulePermission.FILED_ID);
		List<ResourcesModulePermission> modulePermissions = modulePermissionQuery
				.selectByList(this.resourcesModulePermissionRepository);
		List<ResourcesModulePermissionTreeValue> result = new ArrayList<>();
		modules.forEach(module -> {
			if (StringUtils.isNullOrBlank(module.getParentId())) {
				result.addAll(findModulePermissionChilds(module, modules, modulePermissions));
			}
		});
		return result;
	}

	/**
	 * 递归树
	 * 
	 * @param item
	 * @param modules
	 * @param modulePermissions
	 * @return
	 */
	private <P extends IPermissionEntity> List<ResourcesModulePermissionTreeValue> findModulePermissionChilds(
			ResourcesModule item, List<ResourcesModule> modules, List<ResourcesModulePermission> modulePermissions) {
		List<ResourcesModule> moduleList = CollectionUtils.findCollection(modules, (s) -> {
			return item.getId().equalsIgnoreCase(s.getParentId());
		});
		List<ResourcesModulePermissionTreeValue> result = new ArrayList<>();
		ResourcesModulePermissionTreeValue moduleValue = AutoMapUtils.map(item,
				ResourcesModulePermissionTreeValue.class);
		moduleValue.setResourcesId(item.getId());
		moduleValue.setIsGranted(false);
		moduleValue.setPermissionName("");
		modulePermissions.stream().filter(p -> {
			return p.getResourcesId().equalsIgnoreCase(item.getId());
		}).peek(m -> {
			ResourcesModuleOperationPermissionValue opValue = new ResourcesModuleOperationPermissionValue();
			opValue.setPermissionName(m.getName());
			opValue.setIsGranted(false);
			opValue.setFriendlyName(m.getFriendlyName());
			opValue.setSummary(m.getSummary());
			if (moduleValue.getOperationPermissions() == null) {
				moduleValue.setOperationPermissions(new ArrayList<>());
			}
			moduleValue.getOperationPermissions().add(opValue);
		}).collect(Collectors.toList());
		if (moduleList.size() > 0) {
			moduleValue.childrenNullValueInit();
		}
		for (ResourcesModule module : moduleList) {
			moduleValue.getChildren().addAll(findModulePermissionChilds(module, modules, modulePermissions));
		}
		result.add(moduleValue);
		return result;
	}

	/**
	 * 清除模块缓存
	 */
	@Override
	public void clearModuleCache() {
		AutumnCacheContext.clearCache(CACHE_NAME_RESOURCES_MODULE);
		AutumnCacheContext.clearCache(CACHE_NAME_RESOURCES_MODULE_MENU);
		AutumnCacheContext.clearCache(CACHE_NAME_RESOURCES_MODULE_PERMISSION);
		AutumnCacheContext.clearCache(CACHE_NAME_RESOURCES_MODULE_PERMISSION_SHIRO);
	}

	/**
	 * 清除模块权限缓存
	 */
	@Override
	public void clearModulePermissionCache() {
		AutumnCacheContext.clearCache(CACHE_NAME_RESOURCES_MODULE_PERMISSION);
		AutumnCacheContext.clearCache(CACHE_NAME_RESOURCES_MODULE_PERMISSION_SHIRO);
	}

	@Override
	public <P extends IPermissionEntity> List<ResourcesModulePermissionTreeValue> matchByPermissionTree(
			List<P> permissions) {
		return this.loadByPermissionTree(this.queryByPermissionTree(), permissions);
	}

	@Override
	public <P extends IPermissionEntity> List<ResourcesModulePermissionTreeValue> matchByPermissionTree(
			int resourcesType, List<P> permissions) {
		return this.loadByPermissionTree(this.queryByPermissionTree(resourcesType), permissions);
	}

	@Override
	public <P extends IPermissionEntity> List<ResourcesModulePermissionTreeValue> loadByPermissionTree(
			List<ResourcesModulePermissionTreeValue> sources, List<P> matchPermissions) {
		List<ResourcesModulePermissionTreeValue> result = new ArrayList<>();
		if (sources == null || sources.size() == 0) {
			return result;
		}
		for (ResourcesModulePermissionTreeValue source : sources) {
			result.add(this.newModulePermissionTree(source, matchPermissions));
		}
		return result;
	}

	private <P extends IPermissionEntity> ResourcesModulePermissionTreeValue newModulePermissionTree(
			ResourcesModulePermissionTreeValue source, List<P> matchPermissions) {
		boolean isGranted;
		if (matchPermissions == null || matchPermissions.size() == 0) {
			isGranted = false;
		} else {
			isGranted = matchPermissions.stream().anyMatch(p -> {
				return p.getIsGranted() && p.getResourcesId().equalsIgnoreCase(source.getResourcesId())
						&& StringUtils.isNullOrBlank(p.getName());
			});
		}
		ResourcesModulePermissionTreeValue destination = new ResourcesModulePermissionTreeValue();
		destination.setResourcesId(source.getResourcesId());
		destination.setPermissionName(source.getPermissionName());
		destination.setName(source.getName());
		destination.setCustomName(source.getCustomName());
		destination.setIsGranted(isGranted);
		destination.setSummary(source.getSummary());
		if (source.getOperationPermissions() != null) {
			for (ResourcesModuleOperationPermissionValue sourceOpValue : source.getOperationPermissions()) {
				if (matchPermissions == null || matchPermissions.size() == 0) {
					isGranted = false;
				} else {
					isGranted = matchPermissions.stream().anyMatch(p -> {
						return p.getIsGranted() && p.getResourcesId().equalsIgnoreCase(source.getResourcesId())
								&& p.getName().equalsIgnoreCase(sourceOpValue.getPermissionName());
					});
				}
				ResourcesModuleOperationPermissionValue opValue = new ResourcesModuleOperationPermissionValue();
				opValue.setFriendlyName(sourceOpValue.getFriendlyName());
				opValue.setPermissionName(sourceOpValue.getPermissionName());
				opValue.setSummary(sourceOpValue.getSummary());
				opValue.setIsGranted(isGranted);
				if (destination.getOperationPermissions() == null) {
					destination.setOperationPermissions(new ArrayList<>());
				}
				destination.getOperationPermissions().add(opValue);
			}
		}
		if (source.getChildren() != null && source.getChildren().size() > 0) {
			destination.childrenNullValueInit();
			for (ResourcesModulePermissionTreeValue child : source.getChildren()) {
				destination.getChildren().add(this.newModulePermissionTree(child, matchPermissions));
			}
		}
		return destination;
	}

}
